package com.kamistoat.meimeimall.meimeimallmember.service.impl;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.kamistoat.meimeimall.common.To.memberTo.LoginSuccessTo;
import com.kamistoat.meimeimall.common.To.memberTo.RegisterMemberTo;
import com.kamistoat.meimeimall.common.annotation.ClearCache;
import com.kamistoat.meimeimall.common.annotation.WithCache;
import com.kamistoat.meimeimall.common.constant.DatabaseConstant;
import com.kamistoat.meimeimall.common.constant.MemberConstant;
import com.kamistoat.meimeimall.common.constant.ThirdPartyConstant;
import com.kamistoat.meimeimall.common.dubboInterface.ThirdPartyDubboInterface;
import com.kamistoat.meimeimall.common.exception.BizCodeEnum;
import com.kamistoat.meimeimall.common.exception.RRException;
import com.kamistoat.meimeimall.common.utils.R;
import com.kamistoat.meimeimall.meimeimallmember.dao.MemberLevelDao;
import com.kamistoat.meimeimall.meimeimallmember.dao.MemberReceiveAddressDao;
import com.kamistoat.meimeimall.meimeimallmember.entity.MemberLevelEntity;
import com.kamistoat.meimeimall.meimeimallmember.entity.MemberReceiveAddressEntity;
import com.kamistoat.meimeimall.meimeimallmember.service.MemberService;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.regex.Pattern;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.kamistoat.meimeimall.meimeimallmember.dao.MemberDao;
import com.kamistoat.meimeimall.meimeimallmember.entity.MemberEntity;
import org.springframework.transaction.annotation.Transactional;


@Service("memberService")
public class MemberServiceImpl extends ServiceImpl<MemberDao, MemberEntity> implements MemberService {
    protected Logger logger = LoggerFactory.getLogger(MemberServiceImpl.class);

    @Qualifier("flushMemberTestDataThreadPoolExecutor")
    @Autowired
    ThreadPoolExecutor flushMemberTestDataThreadPoolExecutor;

    @Autowired
    MemberLevelDao memberLevelDao;
    @Autowired
    MemberDao memberDao;
    @Autowired
    MemberReceiveAddressDao memberReceiveAddressDao;
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Reference
    ThirdPartyDubboInterface thirdPartyDubboInterface;


    /**
     * 自己写的服务层接口，注册用户信息
     *
     * @param registerMemberTo To
     */
    @Override
    public void registerMember(RegisterMemberTo registerMemberTo) {
        MemberEntity memberEntity = new MemberEntity();
        // 异常机制内容：首先检查手机号是否是唯一的，有的话直接抛异常，后面的直接不做了。
        checkPhoneNumUnique(registerMemberTo.getPhoneNum());

        // 密码加密
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encode = bCryptPasswordEncoder.encode(registerMemberTo.getPassword());
        memberEntity.setPassword(encode);

        // 其他默认逻辑
        memberEntity.setUsername(registerMemberTo.getUsername());
        memberEntity.setMobile(registerMemberTo.getPhoneNum());
        // 将默认的会员等级查出来
        memberEntity.setLevelId(memberLevelDao.selectOne(
                new QueryWrapper<MemberLevelEntity>().eq("default_status", 1)).getId());
        memberEntity.setCreateTime(new Date());
        this.save(memberEntity);
    }

    /**
     * 登录。
     *
     * @param loginAccount 账户
     * @param password     密码
     */
    @Override
    public LoginSuccessTo login(String loginAccount, String password) {
        // 判断登录方式
        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
        boolean phoneAccount = pattern.matcher(loginAccount).matches();
        MemberEntity memberEntity = null;
        if (phoneAccount) {
            // 如果使用手机号登录
            memberEntity = baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("mobile", loginAccount));
        } else {
            // 如果是用用户名登录
            memberEntity = baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("username", loginAccount));
        }
        if (memberEntity == null) {
            // 用户不存在
            throw new RRException(BizCodeEnum.LOGIN_ERROR_NOT_EXIST.getMessage(), BizCodeEnum.LOGIN_ERROR_NOT_EXIST.getCode());
        } else {
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            if (bCryptPasswordEncoder.matches(password, memberEntity.getPassword())) {
                // 登录成功
                LoginSuccessTo loginSuccessTo = new LoginSuccessTo();
                BeanUtils.copyProperties(memberEntity, loginSuccessTo);
                return loginSuccessTo;
            } else {
                // 密码错误
                throw new RRException(BizCodeEnum.LOGIN_ERROR_WRONG_PASSWORD.getMessage(), BizCodeEnum.LOGIN_ERROR_WRONG_PASSWORD.getCode());
            }
        }
    }

    /**
     * 带缓查询用户Entity
     *
     * @param id 用户Id
     */
    @Override
    @WithCache(prefix = "member:entity:#id", isList = false, returnType = MemberEntity.class)
    public MemberEntity getMemberEntityById(Long id) {
        return this.getById(id);
    }

    /**
     * 带缓更新用户Entity
     *
     * @param memberEntity 用户Entity
     */
    @Override
    @ClearCache(prefix = "member:entity:#memberEntity.id")
    @Transactional
    public void updateMemberEntity(MemberEntity memberEntity) {
        this.updateById(memberEntity);
    }

    /**
     * 带缓更新密码
     *
     * @param old_password 旧密码
     * @param new_password 新密码
     */
    @Override
    @ClearCache(prefix = "member:entity:#id")
    @Transactional
    public void updatePassword(Long userId, String old_password, String new_password) {
        MemberEntity member = this.getOne(new QueryWrapper<MemberEntity>().select("id", "password").eq("id", userId));
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        if (!bCryptPasswordEncoder.matches(old_password, member.getPassword())) {
            throw new RRException(BizCodeEnum.ERROR_OLD_PASSWORD.getMessage(), BizCodeEnum.ERROR_OLD_PASSWORD.getCode());
        } else {
            String newPassWord = bCryptPasswordEncoder.encode(new_password);
            this.update(new UpdateWrapper<MemberEntity>().set("password", newPassWord).eq("id", userId));
        }
    }

    /**
     * 修改密码时发送短信验证码
     *
     * @param phoneNum
     */
    @Override
    public void sendCodeWhenFixPassword(String phoneNum) {
        if (StringUtils.isEmpty(phoneNum)) {
            throw new RRException(BizCodeEnum.INVALID_PHONE_NUM.getMessage(), BizCodeEnum.INVALID_PHONE_NUM.getCode());
        }
        // 先看看redis中是否已经存在一个同手机号的验证码
        String oldCode = stringRedisTemplate.opsForValue().get(MemberConstant.SMS_CODE_CACHE_PREFIX + phoneNum);
        if (!StringUtils.isEmpty(oldCode)) {
            // 如果已经存在，把这个验证码的时间拿出来
            Long oldTime = Long.parseLong(oldCode.split("_")[1]);
            if (System.currentTimeMillis() - oldTime < 60000) {
                // 如果是在60秒内重复发送验证码，就不准再发
                throw new RRException(BizCodeEnum.SMS_REPEAT_EXCEPTION.getMessage(), BizCodeEnum.SMS_REPEAT_EXCEPTION.getCode());
            }
        }
        // 生成验证码
        String code = UUID.randomUUID().toString().substring(0, 5);
        // redis存放值，key就是 sms:code:手机号，值就是验证码_当前系统时间
        stringRedisTemplate.opsForValue().set(
                MemberConstant.SMS_CODE_CACHE_PREFIX + phoneNum,
                code + "_" + System.currentTimeMillis(),
                MemberConstant.SMS_CODE_EXPIRE_TIME,
                MemberConstant.SMS_CODE_EXPIRE_UNIT);

        R sendSMSCodeR = thirdPartyDubboInterface.sendSMSCode(phoneNum, code, MemberConstant.SMS_CODE_EXPIRE_TIME, ThirdPartyConstant.SMS_CODE_PASSWORD);
        if (sendSMSCodeR.getCode() != 0) {
            throw new RRException(sendSMSCodeR.getMsg(), sendSMSCodeR.getCode());
        }
    }

    /**
     * 修改密码时验证短信验证码
     *
     * @param phoneNum
     * @param check_code
     */
    @Override
    public void verifyCodeWhenFixPassword(String phoneNum, String check_code) {
        if (StringUtils.isEmpty(phoneNum) || StringUtils.isEmpty(check_code)) {
            throw new RRException(BizCodeEnum.INVALID_PHONE_NUM.getMessage(), BizCodeEnum.INVALID_PHONE_NUM.getCode());
        }
        String key = MemberConstant.SMS_CODE_CACHE_PREFIX + phoneNum;
        if (!stringRedisTemplate.hasKey(key)) {
            throw new RRException(BizCodeEnum.INVALID_MSG_CODE.getMessage(), BizCodeEnum.INVALID_MSG_CODE.getCode());
        } else {
            String value = stringRedisTemplate.opsForValue().get(key);
            String code = value.split("_")[0];
            if (check_code.equals(code)) {
                stringRedisTemplate.delete(key);
            } else {
                throw new RRException(BizCodeEnum.ERROR_MSG_CODE.getMessage(), BizCodeEnum.ERROR_MSG_CODE.getCode());
            }
        }
    }

    /**
     * 生成测试用户数据
     *
     * @param memberNum
     */
    @Override
    public void constructTestMemberData(Integer memberNum) {
        logger.info("生成{}条测试用户数据", memberNum);
        try {
            List<MemberEntity> members = new ArrayList<>();
            List<CompletableFuture<Void>> asyncList = new ArrayList<>();

            String password = new BCryptPasswordEncoder().encode("000000");
            for (int i = 1; i <= memberNum; i++) {
                MemberEntity var1 = new MemberEntity();
                var1.setLevelId(1 + (long) (Math.random() * 3));
                var1.setUsername("测试用户" + i + "用户名");
                var1.setPassword(password);
                var1.setNickname("测试用户" + i + "昵称");
                var1.setMobile("18700009918");
                var1.setIntegration(0);
                var1.setGrowth(0);
                var1.setCreateTime(new Date());
                var1.setUpdateTime(new Date());
                var1.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
                members.add(var1);

                if (i == memberNum || members.size() >= DatabaseConstant.INSERT_BATCH_LIMIT) {
                    List<MemberEntity> temp = new ArrayList<>(members);
                    while(flushMemberTestDataThreadPoolExecutor.getActiveCount() == flushMemberTestDataThreadPoolExecutor.getMaximumPoolSize()){
                        logger.info("线程池达到已开辟最大线程数，当前任务延迟5秒提交");
                        Thread.sleep(5000);
                    }
                    members.clear();
                    asyncList.add(CompletableFuture.runAsync(() -> {
                        memberDao.insertBatch(temp);
                        logger.info("批插入{}条测试用户数据", temp.size());
                        List<MemberReceiveAddressEntity> receiveAddresses = new ArrayList<>();
                        for (MemberEntity member : temp) {
                            MemberReceiveAddressEntity var2 = new MemberReceiveAddressEntity();
                            var2.setMemberId(member.getId());
                            var2.setName(member.getUsername());
                            var2.setPhone(member.getMobile());
                            var2.setPostCode("000000");
                            var2.setProvince("山东省");
                            var2.setCity("青岛市");
                            var2.setRegion("崂山区");
                            var2.setDetailAddress("详细地址1");
                            var2.setDefaultStatus(1);
                            var2.setCreateTime(new Date());
                            var2.setUpdateTime(new Date());
                            var2.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
                            receiveAddresses.add(var2);

                            MemberReceiveAddressEntity var3 = new MemberReceiveAddressEntity();
                            var3.setMemberId(member.getId());
                            var3.setName(member.getUsername());
                            var3.setPhone(member.getMobile());
                            var3.setPostCode("000000");
                            var3.setProvince("四川省");
                            var3.setCity("成都市");
                            var3.setRegion("成华区");
                            var3.setDetailAddress("详细地址2");
                            var3.setDefaultStatus(0);
                            var3.setCreateTime(new Date());
                            var3.setUpdateTime(new Date());
                            var3.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
                            receiveAddresses.add(var3);
                        }
                        memberReceiveAddressDao.insertBatch(receiveAddresses);
                        logger.info("批插入{}条测试用户收货地址", receiveAddresses.size());
                        temp.clear();
                        receiveAddresses.clear();
                    }, flushMemberTestDataThreadPoolExecutor));
                }
            }

            CompletableFuture.allOf(asyncList.toArray(new CompletableFuture[0])).get();
            logger.info("成功生成{}条测试用户数据，及{}条收货地址", memberNum, memberNum * 2);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }




    /**
     * 自己写的方法，检查电话号是否唯一，在registerMember中被调用
     * 当然也可以不单独抽取，直接在registerMember()中这么查，然后抛异常就是了
     *
     * @param phoneNum 手机号
     */
    private void checkPhoneNumUnique(String phoneNum) {
        Integer mobileNum = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phoneNum));
        // 如果相同手机号的数据行数不为0，那就是有问题
        if (mobileNum != 0) {
            throw new RRException(BizCodeEnum.PHONENUM_EXIST_EXCEPTION.getMessage(), BizCodeEnum.PHONENUM_EXIST_EXCEPTION.getCode());
        }
    }


}